import { BaseService, CoolCommException } from '@cool-midway/core';
import { Provide } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import {
  Equal,
  Repository,
  SelectQueryBuilder,
  LessThan,
  Not,
  In,
  Brackets,
} from 'typeorm';
import {
  DemandHallEntity,
  DemandHallStatus,
} from '../entity/hall';
import {
  PublishDemandHallDTO,
  UpdateDemandHallDTO,
  UpdateDemandHallStatusDTO,
  DemandHallPageDTO,
} from '../dto/hall';
import { ProviderProfileEntity } from '../../provider/entity/profile';
import { ProviderInfoEntity } from '../../provider/entity/info';
import { CapacityEntity } from '../../transport/entity/capacity';
import { ProviderCertificateEntity, ProviderCertificateStatus, CertificateType } from '../../provider/entity/certificate';

@Provide()
export class DemandHallService extends BaseService {
  @InjectEntityModel(DemandHallEntity)
  demandHallEntity: Repository<DemandHallEntity>;

  @InjectEntityModel(ProviderProfileEntity)
  providerProfileEntity: Repository<ProviderProfileEntity>;

  @InjectEntityModel(ProviderInfoEntity)
  providerInfoEntity: Repository<ProviderInfoEntity>;

  @InjectEntityModel(CapacityEntity)
  capacityEntity: Repository<CapacityEntity>;

  @InjectEntityModel(ProviderCertificateEntity)
  providerCertificateEntity: Repository<ProviderCertificateEntity>;

  /**
   * 发布需求/运力
   */
  async publish(providerId: number, dto: PublishDemandHallDTO) {
    const teamId = await this.getTeamId(providerId);
    const expireDays = dto.expireDays ?? 7;
    const expireAt = new Date();
    expireAt.setDate(expireAt.getDate() + expireDays);

    const entity = this.demandHallEntity.create({
      ...dto,
      providerId,
      teamId,
      status: DemandHallStatus.OPEN,
      expireDays,
      expireAt,
    });

    return await this.demandHallEntity.save(entity);
  }

  /**
   * 编辑需求/运力
   */
  async updatePublish(providerId: number, dto: UpdateDemandHallDTO) {
    const entity = await this.demandHallEntity.findOne({
      where: { id: Equal(dto.id), providerId: Equal(providerId) },
    });
    if (!entity) {
      throw new CoolCommException('发布不存在或无权操作');
    }

    const editableFields: (keyof UpdateDemandHallDTO)[] = [
      'postType',
      'resourceType',
      'title',
      'description',
      'location',
      'availableTime',
      'budgetAmount',
      'contactName',
      'contactInfo',
      'remark',
      'visibilityRules'
    ];

    const payload: Partial<DemandHallEntity> = {};
    for (const field of editableFields) {
      if (dto[field] !== undefined) {
        (payload as any)[field] = dto[field];
      }
    }

    if (dto.expireDays !== undefined) {
      payload.expireDays = dto.expireDays;
      const expireAt = new Date();
      expireAt.setDate(expireAt.getDate() + dto.expireDays);
      payload.expireAt = expireAt;
    }

    if (Object.keys(payload).length === 0) {
      return this.info(entity.id);
    }

    await this.demandHallEntity.update(entity.id, payload);
    return this.info(entity.id);
  }

  /**
   * 公共大厅列表
   */
  async page(query: DemandHallPageDTO, viewerProviderId?: number) {
    await this.autoExpire();
    const qb = this.demandHallEntity
      .createQueryBuilder('a')
      .orderBy('a.id', 'DESC');

    this.applyFilters(qb, { ...query, status: query.status !== undefined ? query.status : [DemandHallStatus.OPEN, DemandHallStatus.NEGOTIATING] });

    if (viewerProviderId) {
      await this.applyVisibilityFilters(qb, viewerProviderId);
    } else {
      // 未登录只显示无限制的需求
      qb.andWhere('a.visibilityRules IS NULL');
    }

    const result = await this.entityRenderPage(qb, query ?? {});
    result.list = this.normalizeRawRows(result.list);
    return result;
  }

  private async applyVisibilityFilters(qb: SelectQueryBuilder<DemandHallEntity>, providerId: number) {
    const profile = await this.providerProfileEntity.findOne({ where: { providerId } });
    if (!profile) {
      qb.andWhere('a.visibilityRules IS NULL');
      return;
    }

    const cert = await this.providerCertificateEntity.findOne({ where: { providerId, status: ProviderCertificateStatus.PASSED } });
    const isCertified = !!(cert && (cert.type === 'guide-licensed' || (cert.type as string).includes('guide')));

    qb.andWhere(new Brackets(qb2 => {
      qb2.where('a.visibilityRules IS NULL');

      qb2.orWhere(new Brackets(qb3 => {
        // Gender
        if (profile.gender !== undefined) {
           qb3.andWhere(`(JSON_EXTRACT(a.visibilityRules, '$.gender') IS NULL OR JSON_CONTAINS(JSON_EXTRACT(a.visibilityRules, '$.gender'), CAST(:gender AS CHAR)))`, { gender: profile.gender });
        } else {
           qb3.andWhere(`(JSON_EXTRACT(a.visibilityRules, '$.gender') IS NULL)`);
        }

        // Residence
        if (profile.residence) {
           qb3.andWhere(`(JSON_EXTRACT(a.visibilityRules, '$.residence') IS NULL OR JSON_CONTAINS(JSON_EXTRACT(a.visibilityRules, '$.residence'), :residence))`, { residence: `"${profile.residence}"` });
        } else {
           qb3.andWhere(`(JSON_EXTRACT(a.visibilityRules, '$.residence') IS NULL)`);
        }

        // Certified
        qb3.andWhere(`(JSON_EXTRACT(a.visibilityRules, '$.certified') IS NULL OR (JSON_EXTRACT(a.visibilityRules, '$.certified') = true AND :isCertified = true) OR JSON_EXTRACT(a.visibilityRules, '$.certified') = false)`, { isCertified });

        // Age
        if (profile.age) {
           qb3.andWhere(`(JSON_EXTRACT(a.visibilityRules, '$.age') IS NULL OR (JSON_EXTRACT(a.visibilityRules, '$.age.min') <= :age AND JSON_EXTRACT(a.visibilityRules, '$.age.max') >= :age))`, { age: profile.age });
        } else {
           qb3.andWhere(`(JSON_EXTRACT(a.visibilityRules, '$.age') IS NULL)`);
        }
      }));
    }));
  }

  /**
   * 我的发布
   */
  async myPage(providerId: number, query: any) {
    await this.autoExpire();
    const qb = this.demandHallEntity
      .createQueryBuilder('a')
      .where('a.providerId = :providerId', { providerId })
      .orderBy('a.id', 'DESC');

    this.applyFilters(qb, { ...query, status: query.status !== undefined ? [query.status] : [DemandHallStatus.OPEN, DemandHallStatus.NEGOTIATING, DemandHallStatus.CLOSED, DemandHallStatus.EXPIRED] });

    const result = await this.entityRenderPage(qb, query ?? {});
    result.list = this.normalizeRawRows(result.list);
    return result;
  }

  /**
   * 详情
   */
  async info(id: number) {
    const hall = await this.demandHallEntity.findOne({
      where: { id: Equal(id) },
    });
    if (!hall) {
      throw new CoolCommException('需求不存在');
    }

    const profile = await this.providerProfileEntity.findOne({
      where: { providerId: Equal(hall.providerId) },
    });
    const providerInfo = await this.providerInfoEntity.findOne({
      where: { id: Equal(hall.providerId) },
    });

    const showContact =
      hall.status === DemandHallStatus.OPEN ||
      hall.status === DemandHallStatus.NEGOTIATING;

    return {
      ...hall,
      contactInfo: showContact ? hall.contactInfo : undefined,
      contactName: showContact ? hall.contactName : undefined,
      provider: {
        profile,
        info: providerInfo,
      },
    };
  }

  /**
   * 更新状态
   */
  async updateStatus(providerId: number, dto: UpdateDemandHallStatusDTO) {
    const entity = await this.demandHallEntity.findOne({
      where: { id: Equal(dto.id), providerId: Equal(providerId) },
    });
    if (!entity) {
      throw new CoolCommException('发布不存在或无权操作');
    }
    entity.status = dto.status;
    entity.remark = dto.remark ?? entity.remark;
    await this.demandHallEntity.save(entity);
  }

  private async getTeamId(providerId: number) {
    const profile = await this.providerProfileEntity.findOne({
      where: { providerId: Equal(providerId) },
    });
    if (!profile) {
      throw new CoolCommException('请先完善个人资料');
    }
    if (profile.teamId) {
      return profile.teamId;
    }
    if (!profile.capacityId) {
      return null;
    }
    const capacity = await this.capacityEntity.findOne({
      where: { id: Equal(profile.capacityId) },
    });
    return capacity?.teamId ?? null;
  }

  private applyFilters(
    qb: SelectQueryBuilder<DemandHallEntity>,
    query: any = {}
  ) {
    const postType = this.parseNumberFilter(query.postType);
    if (postType !== undefined) {
      qb.andWhere('a.postType = :postType', { postType });
    }

    const resourceType = this.parseNumberFilter(query.resourceType);
    if (resourceType !== undefined) {
      qb.andWhere('a.resourceType = :resourceType', { resourceType });
    }

    const statusFilter = this.parseStatusFilter(query.status);
    if (Array.isArray(statusFilter) && statusFilter.length) {
      qb.andWhere('a.status IN (:...statusList)', { statusList: statusFilter });
    } else if (statusFilter !== undefined) {
      qb.andWhere('a.status = :status', { status: statusFilter });
    } else {
      qb.andWhere('a.status IN (:...availableStatus)', {
        availableStatus: [DemandHallStatus.OPEN, DemandHallStatus.NEGOTIATING],
      });
    }
    qb.andWhere(
      '(a.expireAt IS NULL OR a.expireAt >= :now)',
      { now: new Date() }
    );
    if (query.location) {
      qb.andWhere('a.location LIKE :location', {
        location: `%${query.location}%`,
      });
    }
    if (query.keyword) {
      qb.andWhere('(a.title LIKE :keyword OR a.description LIKE :keyword)', {
        keyword: `%${query.keyword}%`,
      });
    }
  }

  private normalizeRawRows(rows: any[] = []) {
    return rows.map(row => {
      const formatted = {};
      Object.keys(row).forEach(key => {
        const normalizedKey = key.replace(/^[a-z]+_/, '');
        formatted[normalizedKey] = row[key];
      });
      return formatted;
    });
  }

  private parseNumberFilter(value: any) {
    if (value === undefined || value === null || value === '') {
      return undefined;
    }
    const num = Number(value);
    return Number.isNaN(num) ? undefined : num;
  }

  private parseStatusFilter(value: any): number | number[] | undefined {
    if (Array.isArray(value)) {
      const statusList = value
        .map(item => this.parseNumberFilter(item))
        .filter(item => item !== undefined) as number[];
      return statusList.length ? statusList : undefined;
    }
    return this.parseNumberFilter(value);
  }

  private async autoExpire() {
    await this.demandHallEntity.update(
      {
        expireAt: LessThan(new Date()),
        status: Not(In([DemandHallStatus.CLOSED, DemandHallStatus.EXPIRED])),
      },
      {
        status: DemandHallStatus.EXPIRED,
      }
    );
  }
}


